// 该类是对epoll模型进行封装，实现epoll的IO事件添加、删除、修改、获取活跃连接功能
#pragma once
#include "Log.hpp"
#include "Channel.hpp"

#define MAX_EPOLLEVENTS 1024
class Poller
{
private:
    int _epfd;
    struct epoll_event _evs[MAX_EPOLLEVENTS];
    std::unordered_map<int, Channel *> _channels;

private:
    // 对epoll的直接操作
    void Update(Channel *channel, int op)
    {
        // int epoll_ctl(int epfd, int op, int fd, struct epoll_event *ev)
        int fd = channel->Fd();
        struct epoll_event ev;
        ev.data.fd = fd;
        ev.events = channel->Events();
        int ret = epoll_ctl(_epfd, op, fd, &ev);
        if (ret < 0)
        {
            if (op == EPOLL_CTL_DEL)
                LOG_ERROR("EPOLLCTL DEL FAILED!");
            else
                LOG_FATAL("EPOLLCTL ADD/MOD FAILED!");
        }
    }

    // 判断一个Channel 是否在事件监控中
    bool HasChannel(Channel *channel)
    {
        auto it = _channels.find(channel->Fd());
        if (it == _channels.end())
        {
            return false;
        }
        return true;
    }

public:
    Poller()
    {
        _epfd = epoll_create(MAX_EPOLLEVENTS); // 该参数在Linux 2.6.8之后被忽略，但必须大于零。它最初用于提示内核为内部数据结构分配初始大小。
        if (_epfd < 0)
        {
            LOG_FATAL("EPOLL CREATE FAILED!");
            abort(); // abort函数：立即终止程序，不会进行任何清理工作。
        }
    }

    ~Poller()
    {
        close(_epfd);
    }

    // 添加或修改监控事件
    void UpdateEvent(Channel *channel)
    {
        bool ret = HasChannel(channel);
        if (ret == false)
        {
            // 不存在则添加
            _channels.insert(std::make_pair(channel->Fd(), channel));
            return Update(channel, EPOLL_CTL_ADD); // 向epoll实例注册一个新的文件描述符
        }
        return Update(channel, EPOLL_CTL_MOD); // 修改已注册的文件描述符的事件设置
    }

    // 移除监控
    void RemoveEvent(Channel *channel)
    {
        auto it = _channels.find(channel->Fd());
        if (it != _channels.end())
        {
            _channels.erase(it);
        }
        Update(channel, EPOLL_CTL_DEL); // 从epoll实例中删除一个文件描述符
    }

    // 开始监控, 返回活跃连接
    void Poll(std::vector<Channel *> *active)
    {
        // int epoll_wait(int epfd, struct epoll_event *evs, int maxevents, int timeout);
        int nfds = epoll_wait(_epfd, _evs, MAX_EPOLLEVENTS, -1); // -1 阻塞监控，返回准备就绪的文件描述符的数量
        int saveErrno = errno;                                   // 多个wait出错导致全局变量发生变化，需要记录
        if (nfds < 0)
        {
            if (saveErrno == EINTR) // 信号打断
            {
                return;
            }
            errno = saveErrno;
            LOG_FATAL("EPOLL WAIT ERROR:%s\n", strerror(errno));
            abort();
        }

        for (int i = 0; i < nfds; i++) // 添加活跃信息
        {
            auto it = _channels.find(_evs[i].data.fd); // 没找到就说明不在我们的管理之下,这是不正常的
            assert(it != _channels.end());
            it->second->SetREvents(_evs[i].events); // 设置实际就绪的事件
            active->push_back(it->second);
        }
    }
};